/*
 * Copyright 2008-2009 Aying.Net Powered by Fuchun.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.aying.util;

import java.io.File;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Writer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.CharArrayWriter;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Iterator;
import java.util.Collection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 该类是关于 {@code I/O} 操作的工具类。
 * <p>
 * 类中的部份方法及代码来自于 {@code Apache Commons IO 1.3 released}
 * </p>
 * 
 * @author Fuchun
 * @version 1.0, 2009-6-17
 */
public class IOUtils extends Utils {

    /** {@code Unix} 操作系统文件夹分隔符。 */
    public static final char DIR_SEPARATOR_UNIX = '/';

    /** {@code Windows} 操作系统文件夹分隔符。 */
    public static final char DIR_SEPARATOR_WINDOWS = '\\';

    /** 与系统有关的默认名称分隔符。 */
    public static final char DIR_SEPARATOR = File.separatorChar;

    /** {@code Unix} 操作系统换行符。 */
    public static final String LINE_SEPARATOR_UNIX = "\n";

    /** {@code Windows} 操作系统换行符。 */
    public static final String LINE_SEPARATOR_WINDOWS = "\r\n";

    /** 与系统有关的默认名称换行符。 */
    public static final String LINE_SEPARATOR;

    static {
        StringWriter buf = new StringWriter(4);
        PrintWriter out = new PrintWriter(buf);
        out.println();
        LINE_SEPARATOR = buf.toString();
    }

    private static final Log log = LogFactory.getLog(IOUtils.class);

    /** 默认的缓冲区大小。 */
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    /**
     * 受保护的构造方法，防止包外的类调用该类的构造方法。
     */
    protected IOUtils() {
        super();
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.Reader} 字符输入流并释放与之关联的所有资源。见
     * {@link #close(Reader, boolean)}。
     * 
     * @param input 要关闭的字符输入流。
     */
    public static void close(Reader input) {
        close(input, false);
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.Reader} 字符输入流并释放与之关联的所有资源。若
     * {@code errorMessages} 为 {@code true}，则 {@code logging} 中开放 {@code info}
     * 以上的日志级别时，使用 {@code logging} 打印错误信息；否则直接打印出堆栈错误信息。
     * 
     * @param input 要关闭的字符输入流。
     * @param errorMessages 关闭字符输入流出错时，是否打印详细的错误信息。
     */
    public static void close(Reader input, boolean errorMessages) {
        try {
            if (input != null) {
                input.close();
            }
        } catch (IOException ioe) {
            parseCloseErrorMessages(ioe, errorMessages);
        }
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.Writer} 字符输出流并释放与之关联的所有资源。见
     * {@link #close(Writer, boolean)}。
     * 
     * @param output 要关闭的字符输出流。
     */
    public static void close(Writer output) {
        close(output, false);
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.Writer} 字符输出流并释放与之关联的所有资源。若
     * {@code errorMessages} 为 {@code true} 同时 {@code logging} 中开放 {@code info}
     * 以上的日志级别时，使用 {@code logging} 打印错误信息，否则直接打印出堆栈错误信息。
     * 
     * @param output 要关闭的字符输出流。
     * @param errorMessages 关闭字符输入流出错时，是否打印详细的错误信息。
     */
    public static void close(Writer output, boolean errorMessages) {
        try {
            if (output != null) {
                output.close();
            }
        } catch (IOException ioe) {
            parseCloseErrorMessages(ioe, errorMessages);
        }
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.InputStream} 字节输入流并释放与之关联的所有资源。见
     * {@link #close(InputStream, boolean)}。
     * 
     * @param is 要关闭的字节输入流。
     */
    public static void close(InputStream is) {
        close(is, false);
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.InputStream} 字节输入流并释放与之关联的所有资源。若
     * {@code errorMessages} 为 {@code true} 同时 {@code logging} 中开放 {@code info}
     * 以上的日志级别时，使用 {@code logging} 打印错误信息，否则直接打印出堆栈错误信息。
     * 
     * @param is 要关闭的字节输入流。
     * @param errorMessages 关闭字节输入流出错时，是否打印详细的错误信息。
     */
    public static void close(InputStream is, boolean errorMessages) {
        try {
            if (is != null) is.close();
        } catch (IOException ioe) {
            parseCloseErrorMessages(ioe, errorMessages);
        }
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.OutputStream} 字节输出流并释放与之关联的所有资源。见
     * {@link #close(OutputStream, boolean)}。
     * 
     * @param os 要关闭的字节输出流。
     */
    public static void close(OutputStream os) {
        close(os, false);
    }

    /**
     * 无条件的强行关闭指定的 {@code java.io.OutputStream} 字节输出流并释放与之关联的所有资源。若
     * {@code errorMessages} 为 {@code true} 同时 {@code logging} 中开放 {@code info}
     * 以上的日志级别时，使用 {@code logging} 打印错误信息，否则直接打印出堆栈错误信息。
     * 
     * @param os 要关闭的字节输出流。
     * @param errorMessages 关闭字节输出流出错时，是否打印详细的错误信息。
     */
    public static void close(OutputStream os, boolean errorMessages) {
        try {
            if (os != null) os.close();
        } catch (IOException ioe) {
            parseCloseErrorMessages(ioe, errorMessages);
        }
    }

    /* 处理关闭流时的异常错误信息 */
    private static void parseCloseErrorMessages(IOException ioe, boolean errorMessages) {
        if (errorMessages) {
            if (log.isInfoEnabled())
                log.info(ioe.getMessage(), ioe);
            else
                ioe.printStackTrace();
        } else {
            // 忽略任何错误信息，只是使用警告级日志打印信息
            if (log.isWarnEnabled()) log.warn(ioe.getMessage());
        }
    }

    // read toByteArray
    // -----------------------------------------------------------------------
    /**
     * 以字节数组形式返回给定字节输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字节输入流。
     * @return 给定字节输入流中内容的字节数组。
     * @throws NullPointerException 如果给定的字节输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        copy(input, output);
        return output.toByteArray();
    }

    /**
     * 用默认的字符编码，以字节数组形式返回给定字符输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。
     * </p>
     * 
     * @param input 给定的字符输入流。
     * @return 给定字符输入流中默认字符编码的内容字节数组。
     * @throws NullPointerException 如果给定的字符输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static byte[] toByteArray(Reader input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        copy(input, output);
        return output.toByteArray();
    }

    /**
     * 用给定的字符编码，以字节数组形式返回给定字符输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。
     * </p>
     * 
     * @param input 给定的字符输入流。
     * @param encoding 给定的字符编码名称。
     * @return 给定字符输入流中给定字符编码的内容字节数组。
     * @throws NullPointerException 如果给定的字符输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static byte[] toByteArray(Reader input, String encoding) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        copy(input, output, encoding);
        return output.toByteArray();
    }

    // read char[]
    // -----------------------------------------------------------------------
    /**
     * 用默认的字符编码，以字符数组形式返回给定字符输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字节输入流。
     * @return 给定字节输入流中默认字符编码的内容字符数组。
     * @throws NullPointerException 如果给定的字节输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static char[] toCharArray(InputStream input) throws IOException {
        CharArrayWriter output = new CharArrayWriter();
        copy(input, output);
        return output.toCharArray();
    }

    /**
     * 用默认的字符编码，以字符数组形式返回给定字符输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字节输入流。
     * @param encoding 给定的字符编码名称。
     * @return 给定字节输入流中默认字符编码的内容字符数组。
     * @throws NullPointerException 如果给定的字节输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static char[] toCharArray(InputStream input, String encoding) throws IOException {
        CharArrayWriter output = new CharArrayWriter();
        copy(input, output, encoding);
        return output.toCharArray();
    }

    /**
     * 以字符数组形式返回给定字符输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。
     * </p>
     * 
     * @param input 给定的字符输入流。
     * @return 给定字符输入流中的内容字符数组。
     * @throws NullPointerException 如果给定的字符输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static char[] toCharArray(Reader input) throws IOException {
        CharArrayWriter output = new CharArrayWriter();
        copy(input, output);
        return output.toCharArray();
    }

    // read toString
    // -----------------------------------------------------------------------
    /**
     * 用默认的字符编码，以字符串形式返回给定字节输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字节输入流。
     * @return 给定字节输入流中的字符串内容。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO)
     */
    public static String toString(InputStream input) throws IOException {
        StringWriter sw = new StringWriter();
        copy(input, sw);
        return sw.toString();
    }

    /**
     * 用给定的字符编码，以字符串形式返回给定字节输入流中的内容。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字节输入流。
     * @param encoding 给定的字符编码名称。
     * @return 给定字节输入流中的指定字符编码的字符串内容。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO)
     */
    public static String toString(InputStream input, String encoding) throws IOException {
        StringWriter sw = new StringWriter();
        copy(input, sw, encoding);
        return sw.toString();
    }

    /**
     * 以字符串形式返回给定字符输入流中的内容。
     * 
     * @param input 给定的字符输入流。
     * @return 给定字符输入流中的字符串内容。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO)
     */
    public static String toString(Reader input) throws IOException {
        StringWriter sw = new StringWriter();
        copy(input, sw);
        return sw.toString();
    }

    // readLines
    // -----------------------------------------------------------------------
    /**
     * 以 {@code java.util.List<String>} 形式返回给定字符输入流中的内容。列表中的每个元素表示输入流中的一行。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。
     * </p>
     * 
     * @param input 给定的字符输入流。
     * @return 包含给定字符输入流中的字符串内容的 {@code list}。
     * @throws NullPointerException 如果给定的字符输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1) 该方法已被改写为泛型
     */
    public static List<String> readLines(Reader input) throws IOException {
        BufferedReader br = new BufferedReader(input);
        List<String> list = GenericUtils.getLinkedList();
        String line = br.readLine();
        while (line != null) {
            list.add(line);
            line = br.readLine();
        }
        return list;
    }

    /**
     * 以 {@code java.util.List<String>}
     * 形式返回默认字符编码和给定字节输入流中的内容。列表中的每个元素表示输入流中的一行。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字符输入流。
     * @return 包含给定字符输入流中的字符串内容的 {@code list}。
     * @throws NullPointerException 如果给定的字节输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1) 该方法已被改写为泛型
     */
    public static List<String> readLines(InputStream input) throws IOException {
        return readLines(new InputStreamReader(input));
    }

    /**
     * 以 {@code java.util.List<String>}
     * 形式返回给定字符编码和给定字节输入流中的内容。列表中的每个元素表示输入流中的一行。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 给定的字符输入流。
     * @return 包含给定字符输入流中的字符串内容的 {@code list}。
     * @throws NullPointerException 如果给定的字节输入流为 {@code null}。
     * @throws IOException 如果读入或写出过程中发生 I/O 错误。
     * @since IOUtils.class (Apache Commons IO 1.1) 该方法已被改写为泛型
     */
    public static List<String> readLines(InputStream input, String encoding) throws IOException {
        return encoding == null ? readLines(new InputStreamReader(input))
                : readLines(new InputStreamReader(input, encoding));
    }

    // write byte[]
    // -----------------------------------------------------------------------
    /**
     * 将 {@code data.length} 个字节从指定的 {@code byte} 数组写入给定的输出流。
     * 
     * @param data 要写入的数据。
     * @param output {@code OutputStream} 输出流。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void write(byte[] data, OutputStream output) throws IOException {
        if (data != null) {
            output.write(data);
        }
    }

    /**
     * 使用当前默认的字符编码从指定的 {@code byte} 数组写入给定的字符输出流。
     * 
     * @param data 要写入的数据。
     * @param output {@code Writer} 字符输出流。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void write(byte[] data, Writer output) throws IOException {
        if (data != null) {
            output.write(new String(data));
        }
    }

    /**
     * 使用给定的字符编码从指定的 {@code byte} 数组写入给定的字符输出流。
     * 
     * @param data 要写入的数据。
     * @param encoding 给定的字符编码。
     * @param output {@code Writer} 字符输出流。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void write(byte[] data, Writer output, String encoding) throws IOException {
        if (data != null) {
            if (encoding == null) {
                output.write(new String(data));
            } else {
                output.write(new String(data, encoding));
            }
        }
    }

    /**
     * 将 {@code data.length} 个字符从指定的 {@code char} 数组写入给定的输出流。
     * 
     * @param data 要写入的数据。
     * @param output {@code OutputStream} 输出流。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void write(char[] data, OutputStream output) throws IOException {
        if (data != null) {
            output.write((new String(data)).getBytes());
        }
    }

    /**
     * 使用默认的字符编码将 {@code data.length} 个字符从指定的 {@code char} 数组写入给定的输出流。
     * 
     * @param data 要写入的数据。
     * @param output {@code OutputStream} 输出流。
     * @param encoding 给定的字符编码。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void write(char[] data, OutputStream output, String encoding) throws IOException {
        if (data != null) {
            if (encoding == null) {
                write(data, output);
            } else {
                output.write(new String(data).getBytes(encoding));
            }
        }
    }

    /**
     * 将一个以集合形式给定的多行文本，使用给定的换行符和默认的自符编码，写入到一个字节输出流中。
     * 
     * @param lines 一个以集合形式给定的多行文本。
     * @param output 字节输出流对象。
     * @param newLine 换行字符串。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void writeLines(Collection<String> lines, OutputStream output, String newLine)
            throws IOException {
        if (lines == null) return;
        if (newLine == null) {
            newLine = LINE_SEPARATOR;
        }
        for (Iterator<String> it = lines.iterator(); it.hasNext();) {
            String line = it.next();
            if (line != null) {
                output.write(line.toString().getBytes());
            }
            output.write(newLine.getBytes());
        }
    }

    /**
     * 将一个以集合形式给定的多行文本，使用给定的换行符和给定的字符编码，写入到一个字节输出流中。
     * 
     * @param lines 一个以集合形式给定的多行文本。
     * @param output 字节输出流对象。
     * @param newLine 换行字符串。
     * @param encoding 给定的字符编码。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void writeLines(Collection<String> lines, OutputStream output, String newLine, 
            String encoding) throws IOException {
        if (encoding == null) {
            writeLines(lines, output, newLine);
        } else {
            if(lines == null)
                return;
            if (newLine == null) {
                newLine = LINE_SEPARATOR;
            }
            for (Iterator<String> it = lines.iterator(); it.hasNext();) {
                String line = it.next();
                if (line != null) {
                    output.write(line.toString().getBytes(encoding));
                }
                output.write(newLine.getBytes(encoding));
            }
        }
    }
    
    /**
     * 将一个以集合形式给定的多行文本，使用给定的换行符和默认的自符编码，写入到一个字节输出流中。
     * 
     * @param lines 一个以集合形式给定的多行文本。
     * @param newLine 换行字符串。
     * @param output 字符输出流对象。
     * @throws NullPointerException 如果 {@code output} 为 {@code null}。
     * @throws IOException 如果写入过程中发生 I/O 错误。
     */
    public static void writeLines(Collection<String> lines, String newLine, Writer output)
            throws IOException {
        if (lines == null) return;
        if (newLine == null) {
            newLine = LINE_SEPARATOR;
        }
        for (Iterator<String> it = lines.iterator(); it.hasNext();) {
            String line = it.next();
            if (line != null) {
                output.write(line.toString());
            }
            output.write(newLine);
        }
    }

    // copy from InputStream
    // -----------------------------------------------------------------------
    /**
     * 从给定的字节输入流 {@code java.io.InputStream} 中读取大量的字节到给定的字节输出流
     * {@code java.io.OutputStream} 中。该方法通常用来操作容量超过 {@code 2GB} 的读入流。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。
     * </p>
     * 
     * @param input 一个给定的字节输入流。
     * @param output 一个给定的字节输出流。
     * @return 返回读取的字节数。
     * @throws IOException 在字节读取或输出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.3)
     */
    public static long copyLarge(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while ((n = input.read(buffer)) != -1) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

    /**
     * 从给定的字节输入流 {@code java.io.InputStream} 中读取一定量的字节到给定的字节输出流
     * {@code java.io.OutputStream} 中。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。 该方法若操作超过
     * {@code 2GB} 以上的字节时，操作完成时不会返回正确的整型字节数。因此，若要操作超过 {@code 2GB} 以上的字节时，请使用
     * {@link #copyLarge(InputStream, OutputStream)} 方法。
     * </p>
     * 
     * @param input 一个给定的字节输入流。
     * @param output 一个给定的字节输出流。
     * @return 返回读取操作的字节数。
     * @throws IOException 在字节读取或输出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static int copy(InputStream input, OutputStream output) throws IOException {
        long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    /**
     * 从给定的字节输入流 {@code input} 中读取一定量的字节，使用当前系统平台默认的字符编码，到给定的字符输出流
     * {@code output} 中。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。 该方法使用
     * {@code java.io.InputStreamReader} 来包装给定的 字节输入流
     * </p>
     * 
     * @param input 一个给定的字节输入流。
     * @param output 一个给定的字符输出流。
     * @throws IOException 在字节读取或字符写出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static void copy(InputStream input, Writer output) throws IOException {
        InputStreamReader isr = new InputStreamReader(input);
        copy(isr, output);
    }

    /**
     * 从给定的字节输入流 {@code input} 中读取一定量的字节，使用给定的字符编码，到给定的字符输出流 {@code output} 中。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedInputStream}。 该方法使用
     * {@code java.io.InputStreamReader} 来包装给定的字节输入流 {@code input}，并指定字符编码。
     * 字符编码名称列表可以在：<a href="http://www.iana.org/assignments/character-sets"
     * target="_blank">IANA</a> 中找到。
     * </p>
     * 
     * @param input 一个给定的字节输入流。
     * @param output 一个给定的字符输出流。
     * @throws IOException 在字节读取或字符写出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static void copy(InputStream input, Writer output, String encoding) throws IOException {
        if (encoding == null)
            copy(input, output);
        else {
            InputStreamReader isr = new InputStreamReader(input, encoding);
            copy(isr, output);
        }
    }

    // copy from Reader
    // -----------------------------------------------------------------------
    /**
     * 从给定的字符输入流 {@code java.io.Reader} 中读取大量的字节到给定的字符输出流 {@code java.io.Writer}
     * 中。该方法通常用来操作容量超过 {@code 2GB} 的字符读入流。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。
     * </p>
     * 
     * @param input 一个给定的字符输入流。
     * @param output 一个给定的字符输出流。
     * @return 返回读取的字符数。
     * @throws IOException 在字符读取或输出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.3)
     */
    public static long copyLarge(Reader input, Writer output) throws IOException {
        char[] chars = new char[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while ((n = input.read(chars)) != -1) {
            output.write(chars, 0, n);
            count += n;
        }
        return count;
    }

    /**
     * 从给定的字符输入流 {@code java.io.Reader} 中读取一定量的字符到给定的字符输出流
     * {@code java.io.Writer} 中。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。 该方法若操作超过
     * {@code 2GB} 以上的字符时，操作完成时不会返回正确的整型字符数。因此，若要操作超过 {@code 2GB} 以上的字符时，请使用
     * {@link #copyLarge(Reader, Writer)} 方法。
     * </p>
     * 
     * @param input 一个给定的字符输入流。
     * @param output 一个给定的字符输出流。
     * @return 返回读取操作的字符数。
     * @throws IOException 在字符读取或输出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static int copy(Reader input, Writer output) throws IOException {
        long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    /**
     * 从给定的字符输入流 {@code input} 中读取一定量的字符，使用默认的字符编码，到给定的字节输出流 {@code output} 中。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。 该方法使用
     * {@code java.io.OutputStreamWriter} 来包装给定的字节输出流 {@code output}。
     * 字符编码名称列表可以在：<a href="http://www.iana.org/assignments/character-sets"
     * target="_blank">IANA</a> 中找到。
     * </p>
     * 
     * @param input 一个给定的字符输入流。
     * @param output 一个给定的字节输出流。
     * @throws NullPointerException 如果 {@code input} 或者 {@code output} 为
     *             {@code null}。
     * @throws IOException 在字符读取或字节写出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static void copy(Reader input, OutputStream output) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(output);
        copy(input, output);
        osw.flush();
    }

    /**
     * 从给定的字符输入流 {@code input} 中读取一定量的字符，使用给定的字符编码，到给定的字节输出流 {@code output} 中。
     * <p>
     * 该方法中已包含一个内部缓冲区，所以不需要使用 {@code java.io.BufferedReader}。 该方法使用
     * {@code java.io.OutputStreamWriter} 来包装给定的字节输出流 {@code output}，并指定字符编码。
     * 字符编码名称列表可以在：<a href="http://www.iana.org/assignments/character-sets"
     * target="_blank">IANA</a> 中找到。
     * </p>
     * 
     * @param input 一个给定的字符输入流。
     * @param output 一个给定的字节输出流。
     * @throws NullPointerException 如果 {@code input} 或者 {@code output} 为
     *             {@code null}。
     * @throws IOException 在字符读取或字节写出过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1)
     */
    public static void copy(Reader input, OutputStream output, String encoding) throws IOException {
        if (encoding == null)
            copy(input, output);
        else {
            OutputStreamWriter osw = new OutputStreamWriter(output, encoding);
            copy(input, osw);
            osw.flush();
        }
    }

    // equals content
    // -----------------------------------------------------------------------
    /**
     * 测试给定的两个字节输入流中的内容是否相同。
     * <p>
     * 如果给定的字节输入流未使用 {@code java.io.BufferedInputStream} 进行包装，则该方法内部会自动包装它。
     * </p>
     * 
     * @param input1 第一个字节输入流。
     * @param input2 第二个字节输入流。
     * @return 若两个字节输入流中的内容相同，返回 {@code true}，否则为 {@code false}。
     * @throws NullPointerException 两个给定的字节输入流都为 {@code null} 时，抛出此异常。
     * @throws IOException 在字节读取过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1) 原方法名为 {@code contentEquals}
     */
    public static boolean equalsContent(InputStream input1, InputStream input2) throws IOException {
        if (!(input1 instanceof BufferedInputStream)) input1 = new BufferedInputStream(input1);
        if (!(input2 instanceof BufferedInputStream)) input2 = new BufferedInputStream(input2);
        int ch1 = input1.read();
        while (ch1 != -1) {
            int ch2 = input2.read();
            if (ch1 != ch2) {
                return false;
            }
            ch1 = input1.read();
        }
        int ch2 = input2.read();
        return ch2 == -1;
    }

    /**
     * 测试给定的两个字符输入流中的内容是否相同。
     * <p>
     * 如果给定的字符输入流未使用 {@code java.io.BufferedReader} 进行包装，则该方法内部会自动包装它。
     * </p>
     * 
     * @param input1 第一个字符输入流。
     * @param input2 第二个字符输入流。
     * @return 若两个字符输入流中的内容相同，返回 {@code true}，否则为 {@code false}。
     * @throws NullPointerException 两个给定的字符输入流都为 {@code null} 时，抛出此异常。
     * @throws IOException 在字符读取过程中发生 {@code I/O} 错误时，抛出此异常。
     * @since IOUtils.class (Apache Commons IO 1.1) 原方法名为 {@code contentEquals}
     */
    public static boolean equalsContent(Reader input1, Reader input2) throws IOException {
        if (!(input1 instanceof BufferedReader)) input1 = new BufferedReader(input1);
        if (!(input2 instanceof BufferedReader)) input2 = new BufferedReader(input2);
        int ch1 = input1.read();
        while (ch1 != -1) {
            int ch2 = input2.read();
            if (ch1 != ch2) {
                return false;
            }
            ch1 = input1.read();
        }
        int ch2 = input2.read();
        return ch2 == -1;
    }
}
